package com.glsc.ngateway.platform.service.other;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.glsc.ngateway.common.api.common.enums.DictConstant;
import com.glsc.ngateway.common.api.common.ex.GatewayException;
import com.glsc.ngateway.common.api.common.utils.CommonUtils;
import com.glsc.ngateway.common.api.common.utils.PageUtil;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrDeptDto;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrDeptSearchDto;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrEmpDto;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrEmpSearchDto;
import com.glsc.ngateway.common.api.ldap.feign.IFeignLdapService;
import com.glsc.ngateway.common.api.ldap.resp.LdapResponse;
import com.glsc.ngateway.common.api.platform.dto.other.FortressAccountImportDto;
import com.glsc.ngateway.common.api.platform.dto.other.FortressMachineImportDto;
import com.glsc.ngateway.common.api.platform.dto.user.PlatformUserDto;
import com.glsc.ngateway.common.base.domain.mysql.gateway.other.Fortress;
import com.glsc.ngateway.common.base.domain.mysql.gateway.other.IpSystemCode;
import com.glsc.ngateway.common.base.domain.mysql.gateway.other.SystemLicenseExpire;
import com.glsc.ngateway.common.base.domain.mysql.gateway.system.SystemMain;
import com.glsc.ngateway.common.base.domainmapper.FortressMachineMapper;
import com.glsc.ngateway.common.base.repo.mysql.gateway.other.SystemLicenseExpireRepository;
import com.glsc.ngateway.common.base.repo.mysql.gateway.platform.it.FortressRepository;
import com.glsc.ngateway.platform.service.MailService;
import com.glsc.ngateway.platform.service.system.SystemMainService;
import com.glsc.ngateway.platform.utils.AssertOverride;
import com.glsc.ngateway.platform.utils.SpecificationUtil;
import com.glsc.ngateway.platform.utils.report.ReportFileConfigEnum;
import com.glsc.ngateway.platform.utils.report.ReportFileUtil;
import com.glsc.ngateway.platform.utils.report.ReportUploadContext;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class FortressService {

    private static final Logger logger = LoggerFactory.getLogger(FortressService.class);

    @Resource
    private SystemLicenseExpireRepository systemLicenseExpireRepository;

    @Resource
    private MailService mailService;
    @Resource
    private IFeignLdapService iFeignLdapService;
    @Resource
    private SystemMainService systemMainService;
    @Resource
    private FortressRepository fortressRepository;

    @PersistenceContext
    @Qualifier("entityManagerMysql")
    private EntityManager entityManager;

    public SystemLicenseExpire save(SystemLicenseExpire systemLicense) {
        if(StringUtils.isEmpty(systemLicense.getSystemName())){
            throw GatewayException.error("系统名称不能为空");
        }
        systemLicense.setSystemName(StringUtils.trim(systemLicense.getSystemName()));

        //校验管理员邮箱合法性
        if(StringUtils.isNotEmpty(systemLicense.getAdminEmail())){
            String emailStr=systemLicense.getAdminEmail();
            emailStr=emailStr.replace("，",",");//中英文逗号都替换为英文逗号

            String invalidEmail="";
            StringBuilder newEmailStr=new StringBuilder();
            for(String email:emailStr.split(",")){
                email=StringUtils.trim(email);
                newEmailStr.append(","+email);
                if( !CommonUtils.validEmail(email) ){
                    invalidEmail=invalidEmail+","+email;
                }
            }

            if(!"".equals(invalidEmail)) {
                if(invalidEmail.startsWith(",")){
                    invalidEmail=invalidEmail.substring(1);
                }
                throw GatewayException.error("管理员邮箱不合法，非法邮箱："+invalidEmail);
            }

            systemLicense.setAdminEmail(StringUtils.trim(newEmailStr.substring(1)));
        }

        if(null==systemLicense.getSystemId()) {//新增，则检查名称相同的是否已经存在
            List<SystemLicenseExpire> list = systemLicenseExpireRepository.findBySystemName(systemLicense.getSystemName());
            if (null != list && list.size() > 0) {
                if (!(null != systemLicense.getSystemId() && list.get(0).getSystemId() == systemLicense.getSystemId())) {//更新
                    throw GatewayException.error("名称为：" + systemLicense.getSystemName() + "的系统已经存在，如需修改请选择编辑");
                }
            }
        }

        return systemLicenseExpireRepository.save(systemLicense);
    }

    /**
     * 查找实体
     *
     * @param id
     * @return
     */
    public SystemLicenseExpire findById(Integer id) {
        Optional<SystemLicenseExpire> optional = systemLicenseExpireRepository.findById(id);
        if (optional.isPresent()) {
            return optional.get();
        } else {
            return null;
        }
    }

    public void deleteById(Integer id) {
        systemLicenseExpireRepository.deleteById(id);
    }

    /**
     * 分页条件查询
     *
     * @param pageNo
     * @param pageSize
     * @param param
     * @return
     */
    public Page<SystemLicenseExpire> findPage(int pageNo, int pageSize, Map<String, Object> param) {
        Sort.Direction direction = Sort.Direction.DESC;
        String sort = "systemId";

        pageNo= PageUtil.initPageNo(pageNo);
        pageSize=PageUtil.initPageSize(pageSize);

        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, direction, sort);
        return systemLicenseExpireRepository.findAll(SpecificationUtil.buildSpecification(param, SystemLicenseExpire.class), pageable);
    }

    /**
     * 根据条件查询List
     */
    public List<SystemLicenseExpire> findList(Map<String, Object> param) {
        return systemLicenseExpireRepository.findAll(SpecificationUtil.buildSpecification(param, SystemLicenseExpire.class));
    }

    /**
     * 查询全部数据
     */
    public List<SystemLicenseExpire> findAll() {
        return systemLicenseExpireRepository.findAll();
    }

    /**
     * 到期提醒
     */
    public void timeEndNotify() {
        List<SystemLicenseExpire> systemList = this.findAll();

        //邮件已发送信息list
        List<String> sendMailList = new ArrayList<>();


        //遍历产品列表，发送邮件通知
        for (SystemLicenseExpire systemLicenseExpire : systemList) {

            LinkedHashMap<String, String> needCheckExpireMap = new LinkedHashMap<>();//所有需要检查有效期的事件
            LinkedHashMap<String, String> expiredMap = new LinkedHashMap<>();//所有需要检查有效期的事件

            if (!StringUtils.isEmpty(systemLicenseExpire.getSystemLicenseExpire())) {
                needCheckExpireMap.put(systemLicenseExpire.getSystemName(), StringUtils.trim(systemLicenseExpire.getSystemLicenseExpire()));
            }
            if (null != systemLicenseExpire.getModuleSet()) {
                systemLicenseExpire.getModuleSet().forEach(item -> {
                    if (!StringUtils.isEmpty(item.getModuleLicenseExpireDate())) {
                        needCheckExpireMap.put("子模块：" + item.getModuleName(), StringUtils.trim(item.getModuleLicenseExpireDate()));
                    }
                });
            }


            int remindBeforeDays = systemLicenseExpire.getBeforeRemindDays();
            if (remindBeforeDays < 3) {
                remindBeforeDays = 3;//至少提前3天通知
            }

            int finalRemindBeforeDays = remindBeforeDays;
            needCheckExpireMap.forEach((key, value) -> {
                Long betweenDays = CommonUtils.dateDiff(value, CommonUtils.getCurrentDateStr());
                if (null != betweenDays && (betweenDays == finalRemindBeforeDays || (betweenDays < finalRemindBeforeDays && betweenDays % 3 == 0) || betweenDays <= 3)) {//指定提前天数以及提前每3天提醒一次
                    expiredMap.put(key, value);//已经到期
                }
            });

            if (expiredMap.size() == 0) {
                continue;//继续检查下一个
            } else {
                StringBuilder mailContent = new StringBuilder();
                mailContent.append("国联证券" + systemLicenseExpire.getSystemName());
                mailContent.append("系统管理员您好，该系统License授权到期提醒，请您提前处理：<br/>");
                expiredMap.forEach((key, value) -> {
                    mailContent.append(key);
                    mailContent.append("  的License到期日为" + value + "。<br/>");
                });

                List<String> mailList = CommonUtils.mailList(systemLicenseExpire.getAdminEmail(), ",");
                String title = systemLicenseExpire.getSystemName() + " 系统License到期提醒";
                String mailId = mailService.sendMail(mailList.toArray(new String[mailList.size()]), null, title, mailContent.toString(), null);

                logger.info(DictConstant.JOB_REMIN_SYSTEM_LICENSE_EXPIRE + title);
                logger.info(DictConstant.JOB_REMIN_SYSTEM_LICENSE_EXPIRE + mailContent.toString());
            }
        }

        String curSysdate = new DateTime().toString();
        //任务执行后，向管理员发送邮件任务数据
        String content = "当前日期： " + curSysdate + " , 网关系统License到期提醒，邮件共发送：" + sendMailList.size() + "条，接收人信息为： " +
                StringUtil.join(sendMailList.toArray(), "，") + "。";
        mailService.sendMailToAdmin("网关系统License到期提醒", content);
    }

    @SneakyThrows
    @Transactional
    public ReportUploadContext<FortressMachineImportDto, String> upload(MultipartFile file, PlatformUserDto user) throws IOException {
        ReportUploadContext<FortressMachineImportDto, String> fortressMachineContext = ReportUploadContext.<FortressMachineImportDto, String>builder()
                .config(ReportFileConfigEnum.FORTRESS_MACHINE)
                .file(file)
                .check(this::checkCpuStandard)
                .build();
        ReportFileUtil.readSingleExcel(fortressMachineContext);
        AssertOverride.notEmpty(fortressMachineContext.getReadRes(), "sheet0不可为空");

        ReportUploadContext<FortressAccountImportDto, String> fortressAccountContext = ReportUploadContext.<FortressAccountImportDto, String>builder()
                .config(ReportFileConfigEnum.FORTRESS_ACCOUNT)
                .file(file)
                .check(this::checkCpuStandard)
                .build();
        ReportFileUtil.readSingleExcel(fortressAccountContext);
        AssertOverride.notEmpty(fortressAccountContext.getReadRes(), "sheet1不可为空");

        List<IpSystemCode> ipSystemCodeList = systemMainService.queryIpSystemCode();
        Map<String,String> ipSystemCodeMap = ipSystemCodeList.stream().filter(e-> {
            return e!= null && StringUtils.isNotEmpty(e.getIp()) && StringUtils.isNotEmpty(e.getSystemCode());
        }).collect(Collectors.toMap(IpSystemCode::getIp, IpSystemCode::getSystemCode, (key1, key2) -> key2));
        List<SystemMain> systemMainList = systemMainService.queryAll();
        Map<String,String> systemNameMap = systemMainList.stream().collect(Collectors.toMap(SystemMain::getSystemClassNo, SystemMain::getOpName, (key1, key2) -> key2));
        LdapResponse<List<LdapEhrEmpDto>> ehrList = iFeignLdapService.getUserList(new LdapEhrEmpSearchDto());
        Map<String,LdapEhrEmpDto> ehrMap = ehrList.getData().stream().collect(Collectors.toMap(LdapEhrEmpDto::getBadge, Function.identity(), (key1, key2) -> key2));
        LdapResponse<List<LdapEhrDeptDto>> deptList = iFeignLdapService.getDepartmentList(new LdapEhrDeptSearchDto());
        Map<String,LdapEhrDeptDto> deptMap = deptList.getData().stream().map(e->{
            e.setDtitle(e.getDtitle().trim());
            return e;
        }).collect(Collectors.toMap(LdapEhrDeptDto::getDtitle, Function.identity(), (key1, key2) -> key2));
        Map<String,FortressAccountImportDto> accountInfo = fortressAccountContext.getReadRes().stream().collect(Collectors.toMap(FortressAccountImportDto::getAccount, Function.identity(), (key1, key2) -> key2));
        Map<String,List<FortressMachineImportDto>> machineInfo = fortressMachineContext.getReadRes().stream().collect(Collectors.groupingBy(FortressMachineImportDto::getAccount));
        //改成按照账号导入，没有资产的为空
        List<Fortress> fortressList = FortressMachineMapper.INSTANCE.dto2Do(fortressAccountContext.getReadRes());
        List<Fortress> fortressListAdd = new ArrayList<>();
        fortressList.forEach(e -> {
            //ehr匹配上，且部门能找到的人
            if (ehrMap.keySet().contains(e.getAccount()) && !"admin".equals(e.getAccount()) && ehrMap.get(e.getAccount()).getDeptinfo() != null) {
                e.setNotOa(0);
                //对一个用户多个机器的数据进行处理
                fillMultipleMachine(e,machineInfo,ehrMap,ipSystemCodeMap,systemNameMap,fortressListAdd);
            } else if (StringUtils.isNotEmpty(e.getPosition())) {
                //ehr系统匹配不到的用户，用岗位里的值解析后再匹配到部门
                String[] position = e.getPosition().split("&");
                String deptName = position[0];
                String applyName = "";
                if (position.length > 1){
                    applyName = position[1];
                }
                LdapEhrDeptDto dept = deptMap.get(deptName.trim());
                if (dept == null){
                    e.setDepartmentOne("无法匹配人员");
                    fillSystemInfo(e,systemNameMap,ipSystemCodeMap);
                }
                //对一个用户多个机器的数据进行处理
                List<FortressMachineImportDto> machineImportDtoList = machineInfo.get(e.getAccount());
                if (CollectionUtil.isNotEmpty(machineImportDtoList) && machineImportDtoList.size() > 0){
                    for (int i = 0; i < machineImportDtoList.size(); i++) {
                        e.setMachine(machineImportDtoList.get(i).getMachine());
                        Fortress addFortress = new Fortress();
                        fillInfoByPosition(dept,e,systemNameMap,ipSystemCodeMap,applyName);
                        BeanUtils.copyProperties(e, addFortress);
                        addFortress.setMachine(machineImportDtoList.get(i).getMachine());
                        fortressListAdd.add(addFortress);
                    }
                } else {
                    fillInfoByPosition(dept,e,systemNameMap,ipSystemCodeMap,applyName);
                }
            } else {
                e.setDepartmentOne("无法匹配人员");
                //对一个用户多个机器的数据进行处理
                fillMultipleMachine(e,machineInfo,ehrMap,ipSystemCodeMap,systemNameMap,fortressListAdd);
            }
        });
//        fortressList = fortressList.stream().filter(e->{
//            return e.getNotOa() != 1;
//        }).collect(Collectors.toList());
        fortressList.addAll(fortressListAdd);
//        //异步处理
//        deleteAndSave(fortressList);
        fortressRepository.trancateTable();
        fortressRepository.saveAllAndFlush(fortressList);
        logger.info("fortress处理条数为"+fortressList.size());
        return fortressMachineContext;
    }

    private void fillMultipleMachine(Fortress e, Map<String, List<FortressMachineImportDto>> machineInfo, Map<String, LdapEhrEmpDto> ehrMap, Map<String, String> ipSystemCodeMap, Map<String, String> systemNameMap, List<Fortress> fortressListAdd) {
        List<FortressMachineImportDto> machineImportDtoList = machineInfo.get(e.getAccount());
        if (CollectionUtil.isNotEmpty(machineImportDtoList) && machineImportDtoList.size() > 0){
            for (int i = 0; i < machineImportDtoList.size(); i++) {
                e.setMachine(machineImportDtoList.get(i).getMachine());
                Fortress addFortress = new Fortress();
                fillFortressInfo(e,ehrMap,ipSystemCodeMap,systemNameMap);
                BeanUtils.copyProperties(e, addFortress);
                addFortress.setMachine(machineImportDtoList.get(i).getMachine());
                fortressListAdd.add(addFortress);
            }
        } else {
            fillFortressInfo(e,ehrMap,ipSystemCodeMap,systemNameMap);
        }
    }

    private void fillInfoByPosition(LdapEhrDeptDto dept, Fortress e, Map<String, String> systemNameMap, Map<String, String> ipSystemCodeMap, String applyName) {
        if (StringUtils.isEmpty(e.getDepartmentOne())){
            String deparmentOneName = dept.getDepgrade().equals("一级部门") ? dept.getDtitle() : dept.getAtitle();
            try{
                e.setDepartmentOne(deparmentOneName.replaceAll("/","-"));
                e.setDepartmentOneId(dept.getDepgrade().equals("一级部门") ? dept.getDepid() : dept.getAdminid());
                e.setDepartmentTwo(dept.getDtitle());
                e.setDepartmentTwoId(dept.getDepid());
            }catch (Exception exception){
                logger.error("无法匹配部门信息"+e.getAccount()+"--");
                exception.printStackTrace();
                e.setDepartmentOne("无法匹配人员");
            }

        }
        fillSystemInfo(e,systemNameMap,ipSystemCodeMap);
        if (StringUtils.isNotEmpty(applyName)){
            e.setRemark(applyName+"申请");
        }

    }

    private Fortress fillFortressInfo(Fortress e, Map<String, LdapEhrEmpDto> ehrMap, Map<String, String> ipSystemCodeMap, Map<String, String> systemNameMap) {
        //                e.setStatus(accountInfo.get(e.getAccount()).getStatus());
        if (StringUtils.isEmpty(e.getDepartmentOne())){
            String deparmentOneName = ehrMap.get(e.getAccount()).getDeptinfo().getDepgrade().equals("一级部门") ? ehrMap.get(e.getAccount()).getDeptinfo().getDtitle() : ehrMap.get(e.getAccount()).getDeptinfo().getAtitle();
            try{
                e.setDepartmentOne(deparmentOneName.replaceAll("/","-"));
                e.setDepartmentOneId(ehrMap.get(e.getAccount()).getDeptinfo().getDepgrade().equals("一级部门") ? ehrMap.get(e.getAccount()).getDeptinfo().getDepid() : ehrMap.get(e.getAccount()).getDeptinfo().getAdminid());
                e.setDepartmentTwo(ehrMap.get(e.getAccount()).getDeptinfo().getDtitle());
                e.setDepartmentTwoId(ehrMap.get(e.getAccount()).getDeptinfo().getDepid());
            }catch (Exception exception){
                logger.error("无法匹配部门信息"+e.getAccount()+"--");
                exception.printStackTrace();
                e.setDepartmentOne("无法匹配人员");
            }

        }
//                e.setName(ehrMap.get(e.getAccount()).getName());
//                e.setAccountValidityFrom(accountInfo.get(e.getAccount()).getAccountValidityFrom());
//                e.setAccountValidityEnd(accountInfo.get(e.getAccount()).getAccountValidityEnd());
        fillSystemInfo(e,systemNameMap,ipSystemCodeMap);
        return e;
    }

    private void fillSystemInfo(Fortress e, Map<String, String> systemNameMap, Map<String, String> ipSystemCodeMap) {
        if (StringUtils.isNotBlank(e.getMachine())){
            if(e.getMachine().split("\\(").length>1){
                String ip = e.getMachine().split("\\(")[1].split("\\)")[0];
                e.setSystemCode(ipSystemCodeMap.get(ip));
                if(StringUtils.isNotBlank(e.getSystemCode())){
                    e.setSystemName(systemNameMap.get(e.getSystemCode()));
                }
            }
        }
    }

//    @Async
//    private void deleteAndSave(List<Fortress> fortressList) {
//
//    }

    private void checkCpuStandard(FortressAccountImportDto fortressAccountImportDto) {
    }

    private void checkCpuStandard(FortressMachineImportDto fortressMachineImportDto) {
    }

    @SneakyThrows
    public byte[] export(Fortress param) {
        List<Fortress> datas = fortressRepository.findAll();
        for (Fortress e :datas) {
            try {
                entityManager.detach(e);
            } catch (Exception exception) {
                logger.error("entityManager清除缓存error：", exception);
            }
            if("信息技术总部".equals(e.getDepartmentOne())){
                String departmentOne = e.getDepartmentTwo().replaceAll("/","-");
                e.setDepartmentOne(departmentOne);
            }
        }

        Map<String, List<Fortress>> dataMap = datas.stream().filter(e->e != null && e.getDepartmentOne() != null).collect(Collectors.groupingBy(Fortress::getDepartmentOne));
        List<String> sheetNames = dataMap.keySet().stream().collect(Collectors.toList());
        Collections.sort(sheetNames, Collections.reverseOrder());
        List<List<Fortress>> dataList = new ArrayList<>();
        for (String name :sheetNames) {
            Collections.sort(dataMap.get(name), (s1, s2) -> s1.getAccount().compareTo(s2.getAccount()));
            dataList.add(dataMap.get(name));
        }
        return ReportFileUtil.exportMultiSheet(dataList,sheetNames,ReportFileConfigEnum.FORTRESS_EXPORT);
    }

    public Object search(Object paramsObj) {
        JSONObject objParmams = JSONObject.parseObject(JSONObject.toJSONString(paramsObj)).entrySet().stream()
                .filter(entry -> !entry.getValue().toString().isEmpty())
                .collect(JSONObject::new, (obj, entry) -> obj.put(entry.getKey(), entry.getValue()), JSONObject::putAll);

        if (StrUtil.isEmpty(objParmams.getString("orderByDirection"))) {
            objParmams.put("orderByDirection", "ASC");
        }
        if (StrUtil.isEmpty(objParmams.getString("orderByField"))) {
            objParmams.put("orderByField", "id");
        }
        Sort sort = Sort.by(Sort.Direction.fromString(objParmams.getString("orderByDirection")), objParmams.getString("orderByField"));

        Pageable pageable = PageRequest.of(objParmams.getInteger("pageNo") - 1, objParmams.getInteger("pageSize"), sort);

        Fortress paramSearchDto = JSONObject.parseObject(JSONObject.toJSONString(objParmams), Fortress.class);
        Page<Fortress> result = fortressRepository.searchByParam(pageable, paramSearchDto);
//        result.getContent().forEach(e -> {
//            if (e.getCmdbCpuStandard() != null) {
//                e.setPartNum(e.getCmdbCpuStandard().getPartNum());
//            }
//        });
        return result;

    }
}
